////////////////////////////////////////////////////////////////////////////////////////
//
// JSimpleSim - A machine language simulator based on SimpSim by Anne-Gert Bultena.
//
// Copyright (C) 2008 Gerardo Camorlinga, Ryan Fitzgerald, and RJ Gordon
//
// This file is part of JSimpleSim.
//
// JSimpleSim is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// JSimpleSim is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with JSimpleSim.  If not, see <http://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////////////////////

package jsimplesim.machine;

import jsimplesim.model.MemHexTableModel;
import jsimplesim.model.PcHexTableModel;
import jsimplesim.model.IrHexTableModel;
import jsimplesim.model.RegHexTableModel;
// Team Alpha edit:
// Added an import for our new Activation Record/Stack class
import jsimplesim.model.StackModel;
// end Team Alpha edit
import jsimplesim.view.JSimpleSimView;

import org.apache.log4j.*;

/**
 *
 * Static methods used to do the fetch, decode, execute cycle. Didn't want all
 * this logic in the View class so we put it here.
 * 
 * @author f08maroon
 */
public class Machine {

    /**
     *
     */
    public static int SLEEP_MOD = 500;
    /**
     *
     */
    public static int READY = 0;
    /**
     *
     */
    public static int RUNNING = 1;
    /**
     *
     */
    public static int HALTED = 2;
    /**
     *
     */
    public static int INVALID_INSTRUCTION = 3;
    /**
     *
     */
    private static int STATE;
    static Logger logger = Logger.getLogger(Machine.class);
    /**
     *
     */
    public static MemHexTableModel mem;
    /**
     *
     */
    public static PcHexTableModel pc;
    /**
     *
     */
    public static IrHexTableModel ir;
    /**
     *
     */
    public static RegHexTableModel reg;
    
    // Team Alpha edit:
    // Added our new Activation Record/Stack class
    /**
     * Global instance of the Machine's Activation Record stack.
     */
    public static StackModel stk;
    // end Team Alpha edit
    
    /**
     *
     */
    public static JSimpleSimView view;
    /**
     *
     */
    public static byte highBits;

    // Team Alpha edit:
    // Moved this method up from where it was for readability.
    // Also added JavaDoc comment to differentiate it from the other step method.
    /**
     * Parameterless step method for use by internal run method.
     * This step method simply fetches the next instruction and passes it on to be decoded.  
     * It does not activate the disassembly highlight since the run method makes that call after calling this method.
     */
    private static void step() {
        String[] instruction = Machine.fetch();
        logger.debug("the opcode is: " + instruction[0]);


        // step 4:decode & execute instruction in IR
        Machine.decode(instruction);
    }

    /**
     * 
     * @param mem1
     * @param pc1
     * @param ir1
     * @param reg1
     * @param stk1
     * @param view1
     */
    public static void step(MemHexTableModel mem1, PcHexTableModel pc1, IrHexTableModel ir1, RegHexTableModel reg1, StackModel stk1, JSimpleSimView view1) {
        mem = mem1;
        pc = pc1;
        ir = ir1;
        reg = reg1;
        // Team Alpha edit:
        // Added our new Activation Record/Stack class
        stk = stk1;
        // end Team Alpha edit
        view = view1;

        String[] instruction = Machine.fetch();
        view.highlightList();
        logger.debug("the opcode is: " + instruction[0]);


        // step 4:decode & execute instruction in IR
        Machine.decode(instruction);

    }

    /**
     * 
     * @param state
     * @param mem1
     * @param pc1
     * @param ir1
     * @param reg1
     * @param stk1
     * @param view1
     * @throws InterruptedException
     */
    public static void run(int state, MemHexTableModel mem1, PcHexTableModel pc1, IrHexTableModel ir1, RegHexTableModel reg1, StackModel stk1, JSimpleSimView view1) throws InterruptedException {
        mem = mem1;
        pc = pc1;
        ir = ir1;
        reg = reg1;
        // Team Alpha edit:
        // Added our new Activation Record/Stack class
        stk = stk1;
        // end Team Alpha edit
        setSTATE(state);
        view = view1;

        if (getSTATE() == RUNNING) {
            while (getSTATE() == RUNNING) {
                //step(mem, pc, ir, reg, view);
                step();
                view.highlightList();
                int sleep = view.getSlider() * SLEEP_MOD;
                Thread.sleep(sleep);
            }
        }
    }

    private static void decode(String[] instruction) {

        char opCode = instruction[0].charAt(0);
        char a = instruction[1].charAt(0);
        char b = instruction[2].charAt(0);
        char c = instruction[3].charAt(0);
        String bc = instruction[2].substring(0) + instruction[3].substring(0);
        logger.debug("opCode = " + opCode + "; a = " + a + "; b = " + b + "; c = " + c + "; bc = " + bc);

        switch (opCode) {

            case '1':
                //load mem location to reg
                logger.debug("Case 1 entered");
                regMemLoad(a, b, c);
                break;
            case '2':
                //load a value into reg
                //load bc into a
                logger.debug("Case 2 entered");
                directMemLoad(a, bc);
                break;
            case '3':
                //store register into mem location
                logger.debug("Case 3 entered");
                storeRegMem(a, b, c);
                break;
            case '4':
                // move register R value into register S value
                logger.debug("Case 4 entered");
                moveRegReg(b, c);
                break;
            case '5':
                // integer addition
                logger.debug("Case 5 entered");
                addInt(a, b, c);
                break;
            case '6':
            	/* Team Alpha edit by Bartis Hawley-Wall
            	 * removing float support to overload this op-code...
            	 * This will allow us to get up to 16 more instructions,
            	 * and implement the stack-frame support required.
            	 * Previous code preserved in this comment just in case.
                //float addition
                addFloat(a, b, c);
                */
                logger.debug("Case 6 entered; overloaded code (a) next:");
            	switch (a) {
            		case '0':
            			// function call
                        logger.debug("Overload Case 0 entered");
                        call(bc);
            			break;
            		case '1':
            			// function return
                        logger.debug("Overload Case 1 entered");
                        ret();
            			break;
            		case '2':
            			// MACRO call that performs boilerplate frame setup
                        logger.debug("Overload Case 2 entered");
                        macroCall(bc);
            			break;
            		case '3':
            			// MACRO return that performs boilerplate frame teardown
                        logger.debug("Overload Case 3 entered");
                        macroRet();
            			break;
            		case '4':
            			// push register contents to stack
                        logger.debug("Overload Case 4 entered");
                        push(c);
            			break;
            		case '5':
            			// pop memory at Stack Pointer into register
                        logger.debug("Overload Case 5 entered");
                        pop(c);
            			break;
            		case '6':
            			// relative load from Stack Pointer
                        logger.debug("Overload Case 6 entered");
                        stackRelLoad(bc);
            			break;
            		case '7':
            			// relative load from Base Pointer
                        logger.debug("Overload Case 7 entered");
                        baseRelLoad(bc);
            			break;
            		case '8':
            			// not implemented
            		case '9':
            			// not implemented
            		case 'A':
            			// not implemented
            		case 'B':
            			// not implemented
            		case 'C':
            			// not implemented
            		case 'D':
            			// not implemented
            		case 'E':
            			// not implemented
            		case 'F':
            			// not implemented
                        logger.debug("Error! Overload Case 8-F entered! Not implemented!");
            			break;
            		default:
                        // invalid instr, so break cycle
                        logger.debug("Error! Overload Case default entered (invalid instr)");
                        invalid();
                        break;
            	}
            	break;
            case '7':
                // bitwise OR
                logger.debug("Case 7 entered");
                bitOr(a, b, c);
                break;
            case '8':
                // bitwise AND
                logger.debug("Case 8 entered");
                bitAnd(a, b, c);
                break;
            case '9':
                // bitwise XOR
                logger.debug("Case 9 entered");
                bitXor(a, b, c);
                break;
            case 'A':
                // rotate right, right shift
                logger.debug("Case A entered");
                rotateRight(a, c);
                break;
            case 'B':
                // jmpe or straight jump, compares to reg 0
                logger.debug("Case B entered");
                jmpEq(a, bc);
                break;
            case 'C':
                //halt; break;
                logger.debug("Case C entered");
                halt();
                break;
            case 'D':
                // indirect load, specify memory location through another register
                logger.debug("Case D entered");
                indLoad(b, c);
                break;
            case 'E':
                // store a register into mem location specified by register
                logger.debug("Case E entered");
                indStore(b, c);
                break;
            case 'F':
                // jmple
                logger.debug("Case F entered");
                jmpLe(a, bc);
                break;
            default:
                // invalid instr, so break cycle
                logger.debug("Error! Case default entered (invalid instr)");
                invalid();
                break;
        }
    }

    private static String[] fetch() {

        logger.debug("entered fetch()");
        // step 1: grab program counter
        String str = (String) pc.getValueAt(0, 0);
        logger.debug("retrieved the program counter: " + str);

        // get the first character which represents the row
        int row = Integer.parseInt(str.substring(0, 1), 16);
        logger.debug("pc-row = " + row);

        //get the second character which represents the col
        int col = Integer.parseInt(str.substring(1), 16) + 1;
        logger.debug("pc-col = " + col);

        // get the bytes from the memory
        String byte1 = (String) mem.getValueAt(row, col);
        String byte2 = (String) mem.getValueAt(row, col + 1);
        logger.debug("byte1 = " + byte1);
        logger.debug("byte2 = " + byte2);

        // step 2: move instructions located at pc into IR 
        ir.setValueAt(byte1, 0, 0);
        ir.setValueAt(byte2, 0, 1);
        logger.debug("IR set to " + (String) ir.getValueAt(0, 0));

        // step 3: increment program counter
        int hex = Integer.parseInt(str, 16) + 2;
        //convert it back into a hexString
        String hexString = Integer.toHexString(hex);
        pc.setValueAt(hexString, 0, 0);
        logger.debug("program counter set to " + (String) pc.getValueAt(0, 0));

        // use this array for the decode part of the cycle
        String[] instruct = new String[]{byte1.substring(0, 1), byte1.substring(1, 2), byte2.substring(0, 1), byte2.substring(1, 2)};
        logger.debug("instruct = " + instruct[0] + ", " + instruct[1] + ", " + instruct[2] + ", " + instruct[3]);

        return instruct;
    }

    private static void regMemLoad(char reg1, char row, char column) {
        String row1 = row + "";
        String column1 = column + "";
        String reg2 = reg1 + "";

        int row2 = Integer.parseInt(row1, 16);
        int column2 = Integer.parseInt(column1, 16) + 1;
        int reg3 = Integer.parseInt(reg2, 16);

        String memVal = mem.getValueAt(row2, column2) + "";

        logger.debug("Memory location " + row1 + ", " + column1 + " integer: " + memVal);

        reg.setValueAt(memVal, reg3, 0);
        logger.debug("Register value set in regMemLoad");
    }

    private static void directMemLoad(char regist, String value) {
        logger.debug("Direct Memory load entered, value loading is: " + value);
        int register = charToInt(regist);
        reg.setValueAt(value, register, 0);
    }

    private static void storeRegMem(char reg1, char row, char column) {
        //store the value located in reg1 in the memory location
        int register = charToInt(reg1);
        int row1 = charToInt(row);
        int column1 = charToInt(column) + 1;

        String regVal = reg.getValueAt(register, 0) + "";

        mem.setValueAt(regVal, row1, column1);
        logger.debug("storeRegMem value = "+ regVal);

    }

    private static void moveRegReg(char srcReg, char destReg) {
        int srcReg1 = charToInt(srcReg);
        int destReg1 = charToInt(destReg);

        String regVal = reg.getValueAt(srcReg1, 0) + "";

        reg.setValueAt(regVal, destReg1, 0);

    }

    private static void addInt(char destReg, char reg1, char reg2) {
        int destReg1 = charToInt(destReg);
        int regA = charToInt(reg1);
        int regB = charToInt(reg2);

        int op1 = Integer.parseInt(reg.getValueAt(regA, 0) + "", 16);
        int op2 = Integer.parseInt(reg.getValueAt(regB, 0) + "", 16);

        int sum = op1 + op2;

        logger.debug("op1 = " + op1);
        logger.debug("op2 = " + op2);

        logger.debug("Sum = " + sum);

        String hexString = Integer.toHexString(sum);

        reg.setValueAt(hexString, destReg1, 0);
    }

    /**
     * DEPRECATED
     * Adds two float values using 8 bit representation.
     *      0(sign bit) 000(biased exponent) 0000(mantissa with leading one)
     * We use a condensed and modified version of the IEEE Floating point
     * representation. Condensed because we only have 8 bits to work with...
     * @param destReg The register where the sum is stored.
     * @param reg1 The first float number.
     * @param reg2 The second float number.
     */
    @SuppressWarnings("unused")
	private static void addFloat(char destReg, char reg1, char reg2) {

        // need to get access to src and destination registers
        int destReg1 = charToInt(destReg);
        int regA = charToInt(reg1);
        int regB = charToInt(reg2);

        // retrieve the two float values, and put them in a byte, limits 
        // number of bits to 8...which is what we want
        int valA = Integer.parseInt((String) reg.getValueAt(regA, 0), 16);
        int valB = Integer.parseInt((String) reg.getValueAt(regB, 0), 16);
        byte a = (byte) valA;
        byte b = (byte) valB;

        // Perseus:
        // this is a fix for the 0 special case...
        // The reason we added this is because IEEE treats 0 as a special case
        if (valA == 0 || valB == 0){
            if (valA == 0) {
                String hexString = Integer.toHexString(valB);
                reg.setValueAt(hexString, destReg1, 0);
            } else {
                String hexString = Integer.toHexString(valA);
                reg.setValueAt(hexString, destReg1, 0);
            }
            return; //don't do anything else, we're adding 0!
        }

        // figure out if the first bit is on or off, corresponding to (-) or 
        // positive
        byte signA = findSign(a);
        byte signB = findSign(b);
        logger.debug("** signA = " + signA);
        logger.debug("** signB = " + signB);

        byte expA = findExponent(a);
        byte expB = findExponent(b);
        logger.debug("** expA = " + expA);
        logger.debug("** expB = " + expB);

        byte mantA = findMantissa(a);
        byte mantB = findMantissa(b);
        logger.debug("** mantA = " + mantA);
        logger.debug("** mantB = " + mantB);

        byte mantResult = 0;

        // if the two exponents are already the same, and sign doesn't differ
        if (expA == expB && signA == signB) {
            mantA = (byte) (mantA | 16);
            mantB = (byte) (mantB | 16);
            highBits = (byte) (a >> 4);
            mantResult = addMantissa(mantA, mantB);
            logger.debug("***** highBits after addition: " + highBits);
            highBits = (byte) (highBits << 4);
            logger.debug("***** high bits after shift = " + highBits);
            int total = highBits | mantResult;
            total = total << 24;
            total = total >>> 24;
            String hexString = Integer.toHexString(total);
            reg.setValueAt(hexString, destReg1, 0);
        // check if exponents aren't equal, in which case we have to normalize
        // them
        } else if (expA != expB && signA == signB) {
            if (expA > expB) {
                logger.debug("**** shifting mantB");
                int shift = expA - expB;
                highBits = (byte) (a >> 4);
                mantA = (byte) (mantA | 16);
                mantB = (byte) (mantB | 16);
                mantB = (byte) (mantB >> shift);
                logger.debug("**** mantA = " + mantA);
                logger.debug("**** after mantB shift: " + mantB);
                mantResult = addMantissa(mantA, mantB);
                highBits = (byte) (highBits << 4);
                logger.debug("high bits = " + highBits);
                int total = highBits | mantResult;
                logger.debug("total before left shift: "+total);
                total = total << 24;
                logger.debug("total before right shift: "+total);
                total = total >>> 24;
                logger.debug("total after shifts: " + total);
                String hexString = Integer.toHexString(total);
                reg.setValueAt(hexString, destReg1, 0);
            } else {
                int shift = expB - expA;
                logger.debug("expB = " + expB + ", expA = " + expA);
                highBits = (byte) (b >> 4);
                mantA = (byte) (mantA | 16);
                mantB = (byte) (mantB | 16);
                mantA = (byte) (mantA >> shift);
                mantResult = addMantissa(mantA, mantB);
                highBits = (byte) (highBits << 4);
                logger.debug("high bits = " + highBits);
                int total = highBits | mantResult;
                total = total << 24;
                total = total >>> 24;
                String hexString = Integer.toHexString(total);
                reg.setValueAt(hexString, destReg1, 0);
            }
        } else if (expA == expB && signA != signB) {
            // if (signA == 1) {
            logger.debug("byteA is negative so...");
            mantA = (byte) (mantA | 16);
            mantB = (byte) (mantB | 16);
            mantResult = negAddMantissa(mantA, mantB);
            logger.debug("the result after addition = " + mantResult);
            highBits = (byte) (highBits << 4);
            determineSign(a, b);
            int total = highBits | mantResult;
            total = total << 24;
            total = total >>> 24;
            String hexString = Integer.toHexString(total);
            reg.setValueAt(hexString, destReg1, 0);

        } else if (expA != expB && signA != signB) {
            if (expA > expB) {
                // if (signA == 1) {
                logger.debug("byteA is negative so...");

                //need to shift exp B
                int shift = expA - expB;
                highBits = (byte) (a >> 4);
                mantA = (byte) (mantA | 16);
                mantB = (byte) (mantB | 16);
                mantB = (byte) (mantB >> shift);
                mantResult = negAddMantissa(mantA, mantB);
                logger.debug("the result after addition = " + mantResult);
                highBits = (byte) (highBits << 4);
                determineSign(a, b);
                int total = highBits | mantResult;
                total = total << 24;
                total = total >>> 24;
                String hexString = Integer.toHexString(total);
                reg.setValueAt(hexString, destReg1, 0);

            } else if (expA < expB) {
                // if (signA == 1) {
                //need to shift A
                int shift = expB - expA;
                highBits = (byte) (b >> 4);
                logger.debug("highBits = " + highBits);
                mantA = (byte) (mantA | 16);
                mantB = (byte) (mantB | 16);
                mantA = (byte) (mantA >> shift);
                //highBits = (byte) (highBits + shift);
                mantResult = negAddMantissa(mantA, mantB);
                highBits = (byte) (highBits << 4);
                logger.debug("did highBits change here? highBits = " + highBits);
                determineSign(a, b);
                int total = highBits | mantResult;
                total = total << 24;
                total = total >>> 24;
                logger.debug("total is now equal to: " + total);
                String hexString = Integer.toHexString(total);
                reg.setValueAt(hexString, destReg1, 0);

            }
        }


        logger.debug("*********      signA = " + signA);
        logger.debug("*********      signB = " + signB);
        logger.debug("*********      expA = " + expA);
        logger.debug("*********      expB = " + expB);
        logger.debug("*********      mantissaA = " + mantA);
        logger.debug("*********      mantissaB = " + mantB);
        logger.debug("*********      After adding mantissas: mantResult = " + mantResult);
        logger.debug("*************************************");
        logger.debug("byte a = " + a + ", byte b = " + b);

    }

    private static void bitOr(char destReg, char reg1, char reg2) {
        int destReg1 = charToInt(destReg);
        int regA = charToInt(reg1);
        int regB = charToInt(reg2);

        int regAVal = Integer.parseInt(reg.getValueAt(regA, 0) + "", 16);
        int regBVal = Integer.parseInt(reg.getValueAt(regB, 0) + "", 16);

        int orVal = regAVal | regBVal;

        reg.setValueAt(Integer.toHexString(orVal), destReg1, 0);
    }

    private static void bitAnd(char destReg, char reg1, char reg2) {
        int destReg1 = charToInt(destReg);
        int regA = charToInt(reg1);
        int regB = charToInt(reg2);

        int regAVal = Integer.parseInt(reg.getValueAt(regA, 0) + "", 16);
        int regBVal = Integer.parseInt(reg.getValueAt(regB, 0) + "", 16);

        int orVal = regAVal & regBVal;

        reg.setValueAt(Integer.toHexString(orVal), destReg1, 0);
    }

    private static void bitXor(char destReg, char reg1, char reg2) {
        int destReg1 = charToInt(destReg);
        int regA = charToInt(reg1);
        int regB = charToInt(reg2);

        int regAVal = Integer.parseInt(reg.getValueAt(regA, 0) + "", 16);
        int regBVal = Integer.parseInt(reg.getValueAt(regB, 0) + "", 16);

        int orVal = regAVal ^ regBVal;

        reg.setValueAt(Integer.toHexString(orVal), destReg1, 0);
    }

    private static void rotateRight(char destReg, char timesRotated) {
        int destReg1 = charToInt(destReg);
        int n = charToInt(timesRotated);

        if (n > 8) {
            n = n % 8; //for abstractly large rotations, wrap around is not necessary

        }
        int value = Integer.parseInt(reg.getValueAt(destReg1, 0) + "", 16);

        int rotated = (value >>> n) | (value << (8 - n));
        String result = Integer.toHexString(rotated);

        reg.setValueAt(result, destReg1, 0);

    }

    private static void jmpEq(char reg1, String pcValue) {
        int register = charToInt(reg1);
        String regVal = reg.getValueAt(register, 0) + "";
        String regVal0 = reg.getValueAt(0, 0) + "";  //register 0 value

        if (reg1 == '0') {
            pc.setValueAt(pcValue, 0, 0);
        } else if (regVal.equals(regVal0)) {
            pc.setValueAt(pcValue, 0, 0);
        } else {
            //do nothing
        }

    }

    private static void halt() {
        setSTATE(HALTED);
    }

    private static void indLoad(char destReg, char srcReg) {
        int destReg1 = charToInt(destReg);
        int srcReg1 = charToInt(srcReg);

        String memLoc = reg.getValueAt(srcReg1, 0) + "";

        int row = charToInt(memLoc.charAt(0));
        int col = charToInt(memLoc.charAt(1)) + 1;

        String memVal = mem.getValueAt(row, col) + "";

        reg.setValueAt(memVal, destReg1, 0);

    }

    private static void indStore(char srcReg, char memLocReg) {
        int srcReg1 = charToInt(srcReg);
        int memLocReg1 = charToInt(memLocReg);

        String regVal = reg.getValueAt(srcReg1, 0) + "";

        String memLoc = reg.getValueAt(memLocReg1, 0) + "";
        int row = charToInt(memLoc.charAt(0));
        int col = charToInt(memLoc.charAt(1)) + 1;

        mem.setValueAt(regVal, row, col);
    }

    private static void jmpLe(char reg1, String pcValue) {
        logger.debug("performing a jmpLE");
        int register = charToInt(reg1);
        logger.debug("Got Register: " + register);
        int regVal = Integer.parseInt(reg.getValueAt(register, 0) + "", 16);
        logger.debug("The regiser value: " + regVal);
        int regVal0 = Integer.parseInt(reg.getValueAt(0, 0) + "", 16);  //register 0 value
        logger.debug("The value of Reg 0: " + regVal0);

        if (regVal <= regVal0) {
            pc.setValueAt(pcValue, 0, 0);
        } else {
            //do nothing
        }
    }
    /*
     * Spring 2012 Team Alpha
     * Additional op-code methods by Bartis Hawley-Wall
     */
    /**
     * Basic subprogram call that will change execution to the target instruction block.
     * This will retrieve the current value in the Program Counter (PC),
     * increase it to point at the instruction after the call,
     * and push this Return Address (RA) onto the stack to begin the
     * new stack frame; then the address pointing to the subprogram's
     * first instruction is placed in the PC.
     * 
     * @param pcValue
     */
    private static void call(String pcValue) {
    	logger.debug("performing a call");
    	
    	// grabbing the current PC value, using the syntax found in original methods
    	// this value is already pointing to the instruction after the call
    	int returnAddress = Integer.parseInt(pc.getValueAt(0, 0) + "", 16);
    	logger.debug("Got PC value: " + Integer.toHexString(returnAddress));
    	
    	// pushing the RA onto the stack for later return
    	// first, get the Stack Pointer (SP)
        int stackPointer = Integer.parseInt(reg.getValueAt(13, 0) + "", 16);
    	logger.debug("Got Stack Pointer: " + stackPointer);
        
        // decrement it
    	stackPointer--;
    	
    	// halt the machine if SP overflows/underflows the edges of Main Memory
    	if ((stackPointer < 0) || (stackPointer > 255))
    	{
        	logger.error("Error! Got Stack Pointer: " + stackPointer + " after decrement, halting the machine...");
        	halt();
        	return;
    	}
    	
    	// convert to a string, and put it back in the SP register
    	logger.debug("Stack Pointer after decrement: " + stackPointer);
        String memLoc = Integer.toHexString(stackPointer);
        reg.setValueAt(memLoc, 13, 0);
        
        // store the RA in Main Memory where SP points
        String value = Integer.toHexString(returnAddress);
    	logger.debug("RA as Hex: " + value);
        int row = charToInt(memLoc.charAt(0));
        int col = charToInt(memLoc.charAt(1)) + 1;
    	logger.debug("placing at row: " + row + " col:" + col);
        mem.setValueAt(value, row, col);
        
        // update the Activation Record stack
        stk.push(value);
    	
    	// updating the PC to the target address
    	pc.setValueAt(pcValue, 0, 0);
    	logger.debug("exiting call");
    }
    
    /**
     * Basic subprogram return that will resume execution of the preceding code block.
     * We assume that the current stack frame has been cleaned up, so this will pop 
     * the Return Address (RA), located at the Base Pointer + 1, into the Program
     * Counter (PC).
     */
    private static void ret() {
    	logger.debug("performing a return");
    	
    	// grabbing the Base Pointer in Register E and incrementing it
        int basePointer = Integer.parseInt(reg.getValueAt(14, 0) + "", 16);
    	logger.debug("Got BP value: " + basePointer);
        basePointer++;
    	logger.debug("BP + 1: " + basePointer);
        
        // translating to row & column
        String memLoc = Integer.toHexString(basePointer);
    	logger.debug("BP + 1 as String: " + memLoc);
        int row = charToInt(memLoc.charAt(0));
        int col = charToInt(memLoc.charAt(1)) + 1;

        // grabbing the RA from Main Memory
        String memVal = mem.getValueAt(row, col) + "";
    	logger.debug("RA retrieved: " + memVal);
    	
    	// now to tear down the Activation Record stack frame...
    	stk.pop();

    	// updating the PC to the RA
        pc.setValueAt(memVal, 0, 0);
    	logger.debug("exiting return");
    }
    
    /**
     * Macro to perform all boiler-plate functionality for a subprogram call.
     * First the basic call method is executed, then the previous Base Pointer (BP)
     * is pushed to the stack, and the current Stack Pointer (SP) is copied into the BP.
     * 
     * @param pcValue
     */
    private static void macroCall(String pcValue) {
    	logger.debug("performing a MACRO call");
    	
    	// first do a basic call
    	logger.debug("Got a subprogram entry point of: " + pcValue);
    	call(pcValue);
    	
    	// push the old BP
    	push('E');
    	
    	// copy SP into BP
    	moveRegReg('D', 'E');
    	logger.debug("exiting MACRO call");    	
    }
    
    /**
     * Macro to perform all boiler-plate functionality for a subprogram return.
     * The current Base Pointer (BP) is copied into the Stack Pointer (SP), then the
     * previous value of BP is popped off the stack into the BP register.  Finally,
     * the basic return method is executed.
     */
    private static void macroRet() {
    	logger.debug("performing a MACRO return");
    	
    	// copy BP into SP
    	moveRegReg('E', 'D');
    	
    	// pop the old BP back into the BP register
    	pop('E');
    	
    	// lastly, do a basic return
    	ret();
    	logger.debug("exiting MACRO return");
    }
    
    /**
     * Push a register value onto the stack.
     * Decrements the Stack Pointer (SP) and stores the value in the specified
     * register at the location within Main Memory pointed to by SP.
     * 
     * @param srcReg
     */
    private static void push(char srcReg) {
    	logger.debug("performing a push");
    	logger.debug("rchReg = "+ srcReg);
    	
    	// first, get the Stack Pointer (SP)
        int stackPointer = Integer.parseInt(reg.getValueAt(13, 0) + "", 16);
        
        // decrement it
    	stackPointer--;
    	
    	// halt the machine if SP overflows/underflows the edges of Main Memory
    	if ((stackPointer < 0) || (stackPointer > 255))
    	{
        	logger.error("Error! Got Stack Pointer: " + stackPointer + " after decrement, halting the machine...");
        	halt();
        	return;
    	}
    	
    	// convert to a string, and put it back in the SP register
    	logger.debug("Stack Pointer after decrement: " + stackPointer);
        String memLoc = Integer.toHexString(stackPointer);
        reg.setValueAt(memLoc, 13, 0);
    	
    	// grab the value in the register named in the op-code
    	int register = charToInt(srcReg);
        String regVal = reg.getValueAt(register, 0) + "";
    	logger.debug("Got register value: " + regVal);
        
        // convert SP string into row & column in Main Memory
        int row = charToInt(memLoc.charAt(0));
        int column = charToInt(memLoc.charAt(1)) + 1;
    	logger.debug("Got Row/Column: " + row + " / " + column);

        // Store the value
        mem.setValueAt(regVal, row, column);
    	logger.debug("exiting push");
    }
    
    /**
     * Pop a value off the stack into a target register.
     * Copies the value at the current Stack Pointer (SP) into the specified
     * register, and then increments the SP.
     * 
     * @param destReg
     */
    private static void pop(char destReg) {
    	logger.debug("performing a pop");

    	// Get the SP
    	String stackPointer = reg.getValueAt(13, 0) + "";
    	logger.debug("Got Stack Pointer: " + stackPointer);
    	
        // Convert SP into row & column in Main Memory
        int row = charToInt(stackPointer.charAt(0));
        int column = charToInt(stackPointer.charAt(1)) + 1;
    	logger.debug("Got Row/Column: " + row + " / " + column);
    	
    	// Get the value in Main Memory
    	String value = mem.getValueAt(row, column) + "";
    	logger.debug("Got value from Main Memory: " + value);
    	
    	// Copy value into target register
    	int destReg1 = charToInt(destReg);
    	reg.setValueAt(value, destReg1, 0);
    	
    	// Increment SP
    	int sPtr = Integer.parseInt(stackPointer, 16);
    	sPtr++;
    	reg.setValueAt(Integer.toHexString(sPtr), 13, 0);
    	
    	logger.debug("exiting pop");
    }
    
    /**
     * Offset from the current value of the Stack Pointer and load Register C with the value in Main Memory there.
     * Accesses the Stack Pointer (SP), which will be Register D, and adds 
     * the specified offset to the value there, then retrieves the contents 
     * of Main Memory at the location pointed to by the offset value.
     * This does not alter the SP value.
     * 
     * @param offset
     */
    private static void stackRelLoad(String offset) {
    	logger.debug("performing a STACK pointer relative load");
    	
    	// Retrieve the current SP
    	String stackPointer = reg.getValueAt(13, 0) + "";
    	logger.debug("Got Stack Pointer: " + stackPointer);
    	
    	// Add the offset
    	int op1 = Integer.parseInt(stackPointer, 16);
        logger.debug("op1 (SP)     = " + op1);
        int op2 = Integer.parseInt(offset, 16);
        logger.debug("op2 (offset) = " + op2);

        int sum = op1 + op2;
        logger.debug("Sum = " + sum);

        String hexString = Integer.toHexString(sum);
    	
        // Convert (SP + offset) into row & column in Main Memory
        int row = charToInt(hexString.charAt(0));
        int column = charToInt(hexString.charAt(1)) + 1;
    	logger.debug("Got Row/Column: " + row + " / " + column);
    	
    	// Get the value in Main Memory
    	String value = mem.getValueAt(row, column) + "";
    	logger.debug("Got value from Main Memory: " + value);
    	
    	// Copy value into register C
    	reg.setValueAt(value, 12, 0);
    	
    	logger.debug("exiting STACK pointer relative load");
    }
    
    /**
     * Offset from the current value of the Base Pointer and load Register C with the value in Main Memory there.
     * Accesses the Base Pointer (BP), which will be Register E, and adds 
     * the specified offset to the value there, then retrieves the contents 
     * of Main Memory at the location pointed to by the offset value.
     * This does not alter the BP value.
     * 
     * @param offset
     */
    private static void baseRelLoad(String offset) {
    	logger.debug("performing a BASE pointer relative load");
    	
    	// Retrieve the current SP
    	String basePointer = reg.getValueAt(14, 0) + "";
    	logger.debug("Got Stack Pointer: " + basePointer);
    	
    	// Add the offset
    	int op1 = Integer.parseInt(basePointer, 16);
        logger.debug("op1 (BP)     = " + op1);
        int op2 = Integer.parseInt(offset, 16);
        logger.debug("op2 (offset) = " + op2);

        int sum = op1 + op2;
        logger.debug("Sum = " + sum);

        String hexString = Integer.toHexString(sum);
    	
        // Convert (SP + offset) into row & column in Main Memory
        int row = charToInt(hexString.charAt(0));
        int column = charToInt(hexString.charAt(1)) + 1;
    	logger.debug("Got Row/Column: " + row + " / " + column);
    	
    	// Get the value in Main Memory
    	String value = mem.getValueAt(row, column) + "";
    	logger.debug("Got value from Main Memory: " + value);
    	
    	// Copy value into register C
    	reg.setValueAt(value, 12, 0);
    	
    	logger.debug("exiting BASE pointer relative load");
    }
    /*
     * Spring 2012 Team Alpha
     * end of additional overloaded op-codes
     */
    
    private static void invalid() {
        setSTATE(INVALID_INSTRUCTION);
    }

    private static int charToInt(char a) {
        String r = a + "";
        int b = Integer.parseInt(r, 16);
        return b;
    }

    /**
     * Used to determine if the highest bit is set to 1 or 0.
     * @param a The byte that we use to find if the sign bit is on or off.
     * @return A 1 if the sign is negative, or 0 if sign is positive.
     */
    private static byte findSign(byte a) {
        // will return either true or false depending on whether the first bit 
        // is 1 or 0
        if ((a & 128) != 0) {
            return 1;
        } else {
            return 0;
        }
    }

    /**
     * Mask off the exponent part of the float number, and return its value.
     * @param b The byte we use to mask.
     * @return The value of the exponent of the floating point number
     */
    private static byte findExponent(byte b) {
    	// Perseus:
    	// Found an error in this that was causing float 
    	// addition to get incorrect results in some cases
    	// fixed...
    	byte exp = 0;
        if ((b & 64) != 0) {
            exp += -4;
        }
        if ((b & 32) != 0) {
            exp += 2;
        }
        if ((b & 16) != 0) {
            exp += 1;
        }
        return exp;

    }

    /**
     * Finds the value of the manitssa of the float number.
     * @param a The byte representing the float number
     * @return The value of the mantissa.
     */
    private static byte findMantissa(byte a) {
        byte mantissa = 0;
        if ((a & 8) != 0) {
            mantissa += 8;
        }
        if ((a & 4) != 0) {
            mantissa += 4;
        }
        if ((a & 2) != 0) {
            mantissa += 2;
        }
        if ((a & 1) != 0) {
            mantissa += 1;
        }
        return mantissa;
    }

    /**
     * A helper method used to compute the sum of the mantissas.
     * We have to truncate the last bit if we spill over 4 bits on the sum.
     * @param mantA The mantissa for first float number.
     * @param mantB The mantissa for the second float number.
     * @return The normalized sum of the two mantissas.
     */
    private static byte addMantissa(byte mantA, byte mantB) {

        byte result = (byte) (mantA + mantB);

        logger.debug("*** result of addition = " + result);
        if ((result & 32) != 0) {
            logger.debug("*** ended up with bit 6 set to 1...result = " + result);
            result = (byte) (result >> 1);
            logger.debug("*** result of addition = " + result + ", the exp is currently: " + highBits);
            highBits += 1;
            logger.debug("*** exponent increased to: " + highBits);
        }
        byte ret = findMantissa(result);
        logger.debug("*** in addMantissa: result = "+result);
        logger.debug("*** \t and ret = " + ret);
        return ret;
    }

    /**
     * We use this method to add negative values to positive values.
     * @param mantA The mantissa for the first floating point number
     * @param mantB The mantissa for the second floating point number.
     * @return The normalized sum of the two mantissas. 
     */
    private static byte negAddMantissa(byte mantA, byte mantB) {
        // First we convert the mantissa's to two's complement notation
        mantA = (byte) ~mantA;
        mantA = (byte) (mantA + 1);
        mantB = (byte) ~mantB;
        mantB = (byte) (mantB + 1);
        byte result = 0;

        // adding negative numbers is the same as subtracting them
        if (mantB > mantA) {
            result = (byte) (mantB - mantA);
        } else if (mantB < mantA) {
            result = (byte) (mantA - mantB);
        } else {
            highBits = 0;
            return result;
        }

        // If the leading bit is set to zero, need to shift mantissa until we
        // have a one in it's place. this gives us normalized values. Also decre
        // -ment exponent.
        while ((result & 16) == 0) {
            result = (byte) (result << 1);
            logger.debug("highBits before changing exponent bits: " + highBits);
            highBits--;
            logger.debug("highBits after changing exponent bits: " + highBits);
        }

        byte ret = findMantissa(result);
        return ret;

    }

    /**
     * Helper method used to determine whether the sign is positive or negative.
     * @param a The first floating point number.
     * @param b The second floating point number.
     */
    private static void determineSign(byte a, byte b) {
        // Bit trick to get the absolute values of the floating point numbers.
        a = (byte) (a & 0x7F);

        if (a > b) {
            highBits = (byte) (highBits & 0xFF);
        } else {
            highBits = (byte) (highBits & 0x7F);
        //logger.debug("did highBits change after determining sign? highBits = " + highBits);
        }
    }

    public static int getSTATE() {
        return STATE;
    }

    public static void setSTATE(int aSTATE) {
        STATE = aSTATE;
    }
}
